<?php


namespace LeeBai\Handler;


use Hyperf\Di\Annotation\AnnotationCollector;
use Hyperf\HttpServer\Router\DispatcherFactory;
use Hyperf\HttpServer\Router\Handler;
use Hyperf\Utils\Str;
use LeeBai\Annotation\Api;
use LeeBai\Annotation\ApiOperation;
use LeeBai\Annotation\Params;
use LeeBai\Annotation\Responses;
use LeeBai\Util\Util;
use Psr\Container\ContainerInterface;

class CreateDocHandler
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    public function buildDoc($server = 'http', $path = '')
    {
        $factory = $this->container->get(DispatcherFactory::class);
        $router = $factory->getRouter($server);
        $routes = $this->getRouteList($router, $path);
        // App\Controller\TestController::delete] => [[url] => /test/delete,[method] => DELETE]
        $apiOperation = AnnotationCollector::getMethodsByAnnotation(ApiOperation::class);
        $apis = AnnotationCollector::getClassesByAnnotation(Api::class);
        $params = AnnotationCollector::getMethodsByAnnotation(Params::class);
        $apiResponse = AnnotationCollector::getMethodsByAnnotation(Responses::class);
        foreach ($apiOperation as $item) {
            $key = $this->getKey($item);
            if (!isset($routes[$key])) {
                continue;
            }
            $routes[$key]['operation'] = $item['annotation']->toArray();
            $routes[$key]['params'] = [];
        }
        foreach ($params as $item) {
            $key = $this->getKey($item);
            if (!isset($routes[$key])) {
                continue;
            }
            $routes[$key]['params'] = $this->getParams($item['annotation']);
        }

        foreach ($apiResponse as $item) {
            $key = $this->getKey($item);
            if (!isset($routes[$key])) {
                continue;
            }
            $routes[$key]['response'] = $this->getResponse($item['annotation']->toArray());
        }
        return array_values($routes);
    }

    /**
     * 处理参数
     * @return array
     * @var array $annotation AnnotationCollector::getMethodByAnnotation
     */
    private static function getResponse(array $annotation)
    {
        $datas = $annotation['data'];
        $failStatus = $annotation['failStatus'];
        foreach ($datas as $key => $data) {
            $datas[$key] = $data->toArray();
        }
        foreach ($failStatus as $key => $data) {
            $failStatus[$key] = $data->toArray();
        }
        $annotation['data'] = $datas;
        $annotation['failStatus'] = $failStatus;
        return $annotation;
    }

    /**
     * 处理参数
     * @return array
     * @var object $annotation AnnotationCollector::getMethodByAnnotation
     */
    protected static function getParams($annotation)
    {
        $params = [];
        if (is_array($annotation->params)) {
            $params = collect($annotation->params)->reduce(function ($prev, $item) {
                return array_merge(
                    $prev,
                    CreateDocHandler::getParams($item)
                );
            }, []);
        } else {
            $params[] = $annotation->toArray();
        }
        return $params;
    }

    /**
     * class + method 组成key
     * @param array $item AnnotationCollector
     * @return string       class::method
     */
    protected function getKey($item)
    {
        return $item['class'] . '::' . $item['method'];
    }

    /**
     * 获取路由信息
     * @param object $router DispatcherFactory::class
     * @return array
     */
    protected function getRouteList($router, $path)
    {
        $list = [];
        [$staticRouters, $variableRouters] = $router->getData();
        foreach ($staticRouters as $method => $items) {
            foreach ($items as $handler) {
                $this->analyzeHandler($list, $method, $handler, $path);
            }
        }
        foreach ($variableRouters as $method => $items) {
            foreach ($items as $item) {
                if (is_array($item['routeMap'] ?? false)) {
                    foreach ($item['routeMap'] as $routeMap) {
                        $this->analyzeHandler($list, $method, $routeMap[0], $path);
                    }
                }
            }
        }
        return $list;
    }

    protected function analyzeHandler(array &$data, string $method, Handler $handler, ?string $path)
    {
        if (!is_null($path) && !Str::contains($handler->route, $path)) {
            return;
        }
        if (is_array($handler->callback)) {
            $action = implode('::', $handler->callback);
        } elseif (is_string($handler->callback)) {
            $action = str_replace('@', "::", $handler->callback);
        } else {
            return;
        }
        $data[$action] = [
            'url' => $handler->route,
            'method' => $method
        ];
    }
}